在项目根目录下执行:

      -gcflags "-N -l" 是为了关闭编译器优化和函数内联,防止后面在设置断点的时候找不到相对应的代码位置。

      得到了可执行文件 hello,执行:

      进入 gdb 调试模式,执行 info files,得到可执行文件的文件头,列出了各种段:

      1. (gdb) b *0x450e20

      这就是 Go 程序的入口地址,我是在 linux 上运行的,所以入口文件为 src/runtime/rt0_linux_amd64.s,runtime 目录下有各种不同名称的程序入口文件,支持各种操作系统和架构,代码为:

      主要是把 argc,argv 从内存拉到了寄存器。这里 LEAQ 是计算内存地址,然后把内存地址本身放进寄存器里,也就是把 argv 的地址放到了 SI 寄存器中。最后跳转到:

      1. TEXT main(SB),NOSPLIT,$-8
      2. MOVQ $runtime·rt0_go(SB), AX

      继续跳转到 ,位置:/usr/local/go/src/runtime/asm_amd64.s,代码:

      参考文献里的一篇文章【探索 golang 程序启动过程】研究得比较深入,总结下:

      最后用一张图来总结 go bootstrap 过程吧:

      main 函数里执行的一些重要的操作包括:新建一个线程执行 sysmon 函数,定期垃圾回收和调度抢占;启动 gc;执行所有的 init 函数等等。

      上面是启动过程,看一下退出过程:

      1. exit(0)
      2. for {
      3. var x *int32

      关于程序退出这一段的阐述来自群聊《golang runtime 阅读》,又是一个高阶的读源码的组织,github 主页见参考资料。